package com.dangs.downloader.download;

import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import com.dangs.downloader.HttpHelper;
import com.dangs.downloader.L;

import java.io.*;
import java.net.*;
import java.util.concurrent.atomic.AtomicBoolean;

public class Downloader {
    private static final int DEFAULT_THREAD_COUNT = 5;  // 默认线程数量
    private AtomicBoolean canceled; // 取消状态，如果有一个子线程出现异常，则取消整个下载任务
    private DownloadFile file; // 下载的文件对象
    private String storageLocation;
    private final int threadCount; // 线程数量
    private long fileSize; // 文件大小
    private final String url;
    private long beginTime; // 开始时间
    private Logger logger;
    private final String TAG = "Downloader";
    private HttpHelper.DownloadInterface listener;

    public Downloader(String url) {
        this(url, DEFAULT_THREAD_COUNT);
    }

    public Downloader(String url, int threadCount) {
        this.url = url;
        this.threadCount = threadCount;
        this.canceled = new AtomicBoolean(false);
        String dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
        String file = url.substring(url.lastIndexOf('/') + 1);
        if (TextUtils.isEmpty(file)) {
            file = "data.html";
        }
        this.storageLocation = dir + "/" + file;
        L.d(TAG, "download " + url + " with " + threadCount + " thread, save at " + storageLocation);
    }

    public void setListener(HttpHelper.DownloadInterface listener) {
        this.listener = listener;
    }

    public void start() {
//        boolean reStart = new File(storageLocation + ".log").exists();
        boolean reStart = false;
        if (reStart) {
            logger = new Logger(storageLocation + ".log");
            L.d(TAG, "* 继续上次下载进度[已下载：" + logger.getWroteSize() / 1014.0 / 1024 + "], url=" + url);
        } else {
            L.d(TAG, "* 开始下载：" + url);
        }
        fileSize = getFileSize();
        if (fileSize <= 0) {
            if (listener != null) {
                listener.onDownloadErr(new Exception("file size <= 0"));
            }
            return;
        }
        if (listener != null) {
            listener.onGetFileSize(fileSize);
        }
        this.beginTime = System.currentTimeMillis();
        if (listener != null) {
            listener.onStartDownload(beginTime);
        }
        try {
            this.file = new DownloadFile(storageLocation, fileSize, logger);
            if (reStart) {
                file.setWroteSize(logger.getWroteSize());
            }
            dispatcher(reStart);
            printDownloadProgress();
        } catch (IOException e) {
            L.e(TAG, "x 创建文件失败[" + e.getMessage() + "]");
            if (listener != null) {
                listener.onDownloadErr(e);
            }
        }
    }

    /**
     * 分配器，决定每个线程下载哪个区间的数据
     */
    private void dispatcher(boolean reStart) {
        long blockSize = fileSize / threadCount; // 每个线程要下载的数据量
        long lowerBound = 0, upperBound = 0;
        long[][] bounds = null;
        int threadID = 0;
        if (reStart) {
            bounds = logger.getBounds();
        }
        for (int i = 0; i < threadCount; i++) {
            if (reStart) {
                threadID = (int) (bounds[i][0]);
                lowerBound = bounds[i][1];
                upperBound = bounds[i][2];
            } else {
                threadID = i;
                lowerBound = i * blockSize;
                // fileSize-1 !!!!! fu.ck，找了一下午的错
                upperBound = (i == threadCount - 1) ? fileSize - 1 : lowerBound + blockSize;
            }
            new DownloadTask(url, lowerBound, upperBound, file, canceled, threadID).start();
        }
    }

    /**
     * 循环打印进度，直到下载完毕，或任务被取消
     */
    private void printDownloadProgress() {
        long downloadedSize = file.getWroteSize();
        long lastSize = 0;
        while (!canceled.get() && downloadedSize < fileSize) {
            if (listener != null) {
                int progress = (int) (100 * downloadedSize / fileSize);
                float speed = (downloadedSize - lastSize) * 8.0f / 0.5f / 1024 / 1024;
                if (listener != null) {
                    listener.onDownloading(progress, speed);
                }
            }
            lastSize = downloadedSize;
            try {
                Thread.sleep(500);
            } catch (InterruptedException ignore) {

            }
            downloadedSize = file.getWroteSize();
        }
        file.close();
        if (canceled.get()) {
            File file = new File(storageLocation);
            file.deleteOnExit();
            L.e(TAG, "x 下载失败，任务已取消");
            listener.onDownloadErr(new Exception("thread err"));
        } else {
            if (listener != null) {
                listener.onDownloadComplete(System.currentTimeMillis() - beginTime);
            }
        }
    }

    /**
     * @return 要下载的文件的尺寸
     */
    private long getFileSize() {
        if (fileSize != 0) {
            return fileSize;
        }
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) new URL(url).openConnection();
            conn.setConnectTimeout(3000);
            conn.setRequestMethod("HEAD");
            conn.connect();
        } catch (MalformedURLException e) {
            throw new RuntimeException("URL错误");
        } catch (IOException e) {
            L.e(TAG, "x 连接服务器失败[" + e.getMessage() + "]");
            return -1;
        }
        long size = conn.getContentLength();
        Log.d(TAG, "getFileSize:" + size);
        return size;
    }
}